﻿using System;
using System.Linq;
using NetFwTypeLib;

namespace TimerJob.FirewallManager
{
    public class FirewallManager
    {
        private const string BlockPostfix = " -auto";
        private const string ClsidFirewallManager = "{304CE942-6E39-40D8-943A-B913C40C9CD4}";

        // ProgID for the AuthorizedApplication object
        private const string ProgidAuthorizedApplication = "HNetCfg.FwAuthorizedApplication";

        public bool IsEnableFirewall
        {
            get
            {
                INetFwMgr manager = GetFirewallManager();
                bool isFirewallEnabled = manager.LocalPolicy.CurrentProfile.FirewallEnabled;

                return isFirewallEnabled;
            }
        }

        private static INetFwMgr GetFirewallManager()
        {
            Type objectType = Type.GetTypeFromCLSID(new Guid(ClsidFirewallManager));
            return Activator.CreateInstance(objectType) as INetFwMgr;
        }

        public static bool AuthorizeApplication(string title, string applicationPath, NET_FW_SCOPE_ scope, NET_FW_IP_VERSION_ ipVersion, bool enable)
        {
            var auth = NetFwAuthorizedApplication(title, applicationPath, scope, ipVersion, enable);

            INetFwMgr manager = GetFirewallManager();
            try
            {
                manager.LocalPolicy.CurrentProfile.AuthorizedApplications.Add(auth);
            }
            catch (Exception ex)
            {
                return false;
            }

            return true;
        }

        public static bool RemoveApplicationAuth(string applicationPath)
        {
            INetFwMgr manager = GetFirewallManager();
            try
            {
                if (manager.LocalPolicy.CurrentProfile.AuthorizedApplications.OfType<INetFwAuthorizedApplication>().Any(item => item.ProcessImageFileName.Equals(applicationPath, StringComparison.CurrentCultureIgnoreCase)))
                {
                    manager.LocalPolicy.CurrentProfile.AuthorizedApplications.Remove(applicationPath);
                }
            }
            catch (Exception ex)
            {
                return false;
            }

            return true;
        }

        private static INetFwAuthorizedApplication NetFwAuthorizedApplication(string title, string applicationPath, NET_FW_SCOPE_ scope, NET_FW_IP_VERSION_ ipVersion, bool enable)
        {
            // Create the type from prog id
            Type type = Type.GetTypeFromProgID(ProgidAuthorizedApplication);
            var auth = Activator.CreateInstance(type) as INetFwAuthorizedApplication;
            auth.Name = title;
            auth.ProcessImageFileName = applicationPath;
            auth.Scope = scope;
            auth.IpVersion = ipVersion;
            auth.Enabled = enable;

            return auth;
        }

        public static bool IsRuleExists(string filePath)
        {
            var firewallPolicy = Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2")) as INetFwPolicy2;
            if (firewallPolicy == null)
            {
                throw new TypeInitializationException("HNetCfg.FwPolicy2", null);
            }

            var exists = firewallPolicy.Rules.OfType<INetFwRule2>().Any(r => r.ApplicationName.Contains(filePath));

            return exists;
        }

        public static bool AddRule(string title, string applicationPath, bool block, bool enable, bool isIngoingConnection)
        {
            var firewallPolicy = Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2")) as INetFwPolicy2;
            if (firewallPolicy == null)
            {
                throw new TypeInitializationException("HNetCfg.FwPolicy2", null);
            }

            var firewallRule = Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FWRule")) as INetFwRule2;
            if (firewallRule == null)
            {
                throw new TypeInitializationException("HNetCfg.FWRule", null);
            }

            firewallRule.ApplicationName = applicationPath;
            firewallRule.Name = title + BlockPostfix;
            firewallRule.Description = title;
            firewallRule.Action = block ? NET_FW_ACTION_.NET_FW_ACTION_BLOCK : NET_FW_ACTION_.NET_FW_ACTION_ALLOW;
            firewallRule.Direction = isIngoingConnection ? NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN : NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_OUT;
            firewallRule.Enabled = true;
            firewallRule.InterfaceTypes = "All";

            firewallPolicy.Rules.Add(firewallRule);

            return true;
        }

        public static bool RemoveRule(string applicationPath, bool onlyOverrided)
        {
            bool deleted = false;

            var firewallPolicy = Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2")) as INetFwPolicy2;
            if (firewallPolicy == null)
            {
                throw new TypeInitializationException("HNetCfg.FwPolicy2", null);
            }

            //var rule = firewallPolicy.Rules.OfType<INetFwRule2>().FirstOrDefault(r => r.ApplicationName.Contains(applicationPath));

            foreach (var netFwRule in firewallPolicy.Rules)
            {
                var rule2 = netFwRule as INetFwRule2;
                if (rule2 != null && rule2.ApplicationName.Contains(applicationPath))
                {
                    if (onlyOverrided)
                    {
                        if (rule2.Name.Contains(BlockPostfix))
                        {
                            firewallPolicy.Rules.Remove(rule2.Name);
                            deleted = true;
                        }

                    }
                    else
                    {
                        firewallPolicy.Rules.Remove(rule2.Name);
                        deleted = true;
                    }
                }
            }

            return deleted;
        }

        public void TurnFirewall(bool isOn)
        {
            INetFwMgr manager = GetFirewallManager();
            bool isFirewallEnabled = manager.LocalPolicy.CurrentProfile.FirewallEnabled;
            if (isFirewallEnabled != isOn)
            {
                manager.LocalPolicy.CurrentProfile.FirewallEnabled = isOn;
            }
        }
    }
}